package erfgame.core.util;

public final class RangeUtils {

	public static final int[] intersection( int aMin, int aMaxPlus1, int bMin, int bMaxPlus1 ) {
		return new int[]{
			Math.max( aMin, bMin ), 
			Math.min( aMaxPlus1, bMaxPlus1 )
		};
	}
	
	public static final boolean overlaps( int aMin, int aMaxPlus1, int bMin, int bMaxPlus1 ) {
		boolean aMinGreaterEqualbMin = aMin >= bMin;
		boolean aMaxPlus1LessThanEqualbMaxPlux1 = aMaxPlus1 <= bMaxPlus1;
		return aMinGreaterEqualbMin && aMin < bMaxPlus1 || aMaxPlus1 > bMin && aMaxPlus1LessThanEqualbMaxPlux1 || !aMinGreaterEqualbMin && !aMaxPlus1LessThanEqualbMaxPlux1;
	}
	
	public static final int unionRange( short[] into, int intoLength, short[] with, int withLength ) {
		int intoIndex = 0;
		int withIndex = 0;
		int count = intoLength;
		boolean canHaveDuplicates = false;
		while ( count > intoIndex && withLength > withIndex ) {
			short istart = into[intoIndex];
			short iend = into[intoIndex+1];
			short wstart = (short)(with[withIndex]);
			short wend = (short)(with[withIndex+1]);
			
			//   I   W
			//0  |    
			//1  *   
			//2      |
			//3      *
			boolean iendLessThanWstart = iend < wstart;
			if( iendLessThanWstart ) {
				// move forward and let the next iteration handle it
				intoIndex += 2;
				continue;
			}

			//   I   W
			//0      |
			//1      *
			//2  |   
			//3  *
			boolean wendLessThanIstart = wend < istart;
			if( wendLessThanIstart ) {
				// insert before
				count += 2;
				for( int i=count; i>intoIndex+2; i-=2 ) {
					into[ i-1 ] = into[ i-3 ];
					into[ i-2 ] = into[ i-4 ];
				}
				into[ intoIndex ] = wstart;
				into[ intoIndex + 1 ] = wend;
				intoIndex += 2;
				withIndex += 2;
				continue;

			}
			
			//   I   W
			//0      |
			//1  |   *
			//2  *   
			boolean wstartLessThanIstart = wstart < istart;
			boolean istartLessThanEqWend = !wendLessThanIstart;
			boolean wendLessThanEqIend = wend <= iend;
			if( wstartLessThanIstart && istartLessThanEqWend && wendLessThanEqIend ) {
				// union
				short start = wstart;
				//short end = iend;
				into[ intoIndex ] = start;
				//this is unchanged...   
				//into[ intoIndex + 1 ] = end;
				withIndex += 2;
				continue;
			}
			
			//   I   W
			//0  |   
			//1  *   |
			//2      *
			boolean istartLessThanWstart = istart < wstart;
			boolean wstartLessThanEqIend = !iendLessThanWstart;
			boolean iendLessThanEqWend = iend <= wend;
			if( istartLessThanWstart && wstartLessThanEqIend && iendLessThanEqWend ) {
				// union
				//short start = istart;
				short end = wend;
				//into[ intoIndex ] = start;
				into[ intoIndex+1 ] = end;
				intoIndex += 2;
				withIndex += 2;
				canHaveDuplicates = true;
				continue;
			}
			
			//  I   W
			//0     |
			//1 |   |
			//2 *   |
			//3     * 
			boolean wstartLessThanEqIstart = !istartLessThanWstart;
			if( iendLessThanEqWend && wstartLessThanEqIstart ) {
				// overwrite
				short start = wstart;
				short end = wend;
				into[ intoIndex ] = start;
				into[ intoIndex + 1 ] = end;
				withIndex += 2;
				intoIndex += 2;
				canHaveDuplicates = true;
				continue;
			}
			
			//  I   W
			//0 |    
			//1 |   |
			//2 |   *
			//3 *     
			boolean istartLessThanEqWstart = istart <= wstart;
			if( istartLessThanEqWstart && wendLessThanEqIend ) {
				// ignore
				withIndex += 2;
				continue;
			}
		}
		
		// filter out duplicates 
		if( canHaveDuplicates ) {
			for( int i=0; i<count-2; i+= 2 ) {
				if( into[i+1] >= into[i+2] ) {
					into[ i+1 ] = into[ i+3 ];
					// shuffle down and shrink
					for( int j=i+2; j<count-2; j+=2 ) {
						into[ j ] = into[ j+2 ];
						into[ j+1 ] = into[ j+3 ];
					}
					count-=2;
				}
			}
		}
		
		for( int i=withIndex; i<withLength; i+=2 ) {
			into[ count ] = with[ i ];
			into[ count + 1 ] = with[ i + 1 ];
			count+=2;
		}
		
		return count;
		
	}
	
	public static final int subtractRange( short[] surfacesFrom, short[] surfacesSubtract, int surfacesSubtractLength, short[] result ) {
		
		int fromIndex = 0;
		int subtractIndex = 0;
		int count = surfacesFrom.length;
		System.arraycopy( surfacesFrom, 0, result, 0, count );
		while ( count > fromIndex && surfacesSubtractLength > subtractIndex ) {
			short fstart = result[fromIndex];
			short fend = result[fromIndex+1];
			short sstart = (short)(surfacesSubtract[subtractIndex]);
			short send = (short)(surfacesSubtract[subtractIndex+1]);
			
			//   F   S
			//0      |
			//1  |   *
			//2  *
			boolean sendLessThanEqFstart = send <= fstart;
			if( sendLessThanEqFstart ) {
				// skip to the next
				subtractIndex += 2;
				continue;					
			}

			//   F   S
			//0  |    
			//1  *   |
			//2      *
			boolean fendLessThanEqSstart = fend <= sstart;
			if( fendLessThanEqSstart ) {
				fromIndex += 2;
				continue;
			}
			
			//   F   S
			//0      |
			//1  |   |
			//2  |   *
			//3  *
			boolean fstartLessThanSend = !sendLessThanEqFstart;
			boolean sstartLessThanEqFstart = sstart <= fstart;
			boolean sendLessThanFend = send < fend;
			if( fstartLessThanSend && sstartLessThanEqFstart && sendLessThanFend ) {
				// chop
				short start = send;
				short end = fend;
				result[ fromIndex ] = start;
				result[ fromIndex + 1 ] = end;
				subtractIndex += 2;
				continue;
			}
			
			//   F   S
			//0  |   
			//1  |   |
			//2  *   |
			//3      *
			boolean sstartLessThanFend = !fendLessThanEqSstart;
			//boolean fstartLessThanSstart = fstart < sstart;
			boolean fstartLessThanSstart = !sstartLessThanEqFstart;
			//boolean fendLessThanEqSend = fend <= send;
			boolean fendLessThanEqSend = !sendLessThanFend;
			if( sstartLessThanFend && fstartLessThanSstart && fendLessThanEqSend ) {
				// chop
				short start = fstart;
				short end = sstart;
				result[ fromIndex ] = start;
				result[ fromIndex+1 ] = end;
				fromIndex += 2;
				continue;
			}
			
			//  F   S
			//0     |
			//1 |   |
			//2 *   |
			//3     * 
			if( fendLessThanEqSend && sstartLessThanEqFstart ) {
				// destroy
				count -= 2;
				for( int i=fromIndex; i<count; i+=2 ) {
					result[i] = result[i+2];
					result[i+1] = result[i+3];
				}
				continue;
			}
			
			//  F   S
			//0 |    
			//1 |   |
			//2 |   *
			//3 *     
			if( fstartLessThanSstart && sendLessThanFend ) {
				// split
				for( int i=count; i>fromIndex+2; i-=2 ) {
					result[ i ] = result[ i-2 ];
					result[ i+1 ] = result[ i-1 ];
				}
				count+=2;
				result[ fromIndex     ] = fstart;
				result[ fromIndex + 1 ] = sstart;
				result[ fromIndex + 2 ] = send;
				result[ fromIndex + 3 ] = fend;
				fromIndex += 2;
				continue;
			}
		}
		
		return count;
	}
	
	
}
